home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_100 / 111_01 / sq.c < prev    next >
Text File  |  1985-08-19  |  7KB  |  255 lines

  1. /*
  2. HEADER:        ;
  3. TITLE:        Squeezer;
  4. VERSION:    1.5;
  5. DATE:        08/29/1981;
  6.  
  7. DESCRIPTION:    "SQ.C is a data compression utility, which will reduce
  8.         file sizes up to about 45 percent without losing data.
  9.         For compiling and operating instructions, see the file
  10.         SQUEEZER.DOC.";
  11.  
  12. KEYWORDS:    Data compression, squeeze, unsqueeze;
  13. SYSTEM:        CP/M-80;
  14. FILENAME:    SQ.C;
  15. SEE-ALSO    USQ.C, FLS.C, SQUEEZER.DOC;
  16. AUTHORS:    Dick Greenlaw;
  17. COMPILERS:    BDS C;
  18. */
  19. /**********************************************************************
  20.  
  21.  * This program compresses a file without losing information.
  22.  * The usq.com program is required to unsqueeze the file
  23.  * before it can be used.
  24.  *
  25.  * Typical compression rates are:
  26.  *    .COM    6%    (Don't bother)
  27.  *    .ASM    33%    (using full ASCII set)
  28.  *    .DIC    46%    (using only uppercase and a few others)
  29.  * Squeezing a really big file takes a few minutes.
  30.  *
  31.  * Useage:
  32.  *    SQ item ...
  33.  * where ... represents more (optional) items and
  34.  * "item" is either:
  35.  *    drive:        to change the output drive
  36.  *    file        input file
  37.  *    drive:file    input file
  38.  *    -        toggle debugging display mode
  39.  *
  40.  * If no such items are given on the command line you will be
  41.  * prompted for commands (one at a time). An empty command
  42.  * terminates the program.
  43.  *
  44.  * SQ uses the dio package, so input and output can be redirected
  45.  * by special items on the command line such as:
  46.  *    <file        reads console input from file
  47.  *    >file        sends console output to file
  48.  *    +file        sends console output to console and file
  49.  * Also console output of another program using dio can be piped
  50.  * to the input of this one or vice-versa. Example:
  51.  *    A>fls parameters |sq
  52.  * where fls might be a program that expands patterns like *.com
  53.  * to a list of specific file names for sq to squeeze.
  54.  *
  55.  * The squeezed file name is formed by changing the second
  56.  * letter of the file type to Q. If there is no file type,
  57.  * the squeezed file type is QQQ. If the name exists it is
  58.  * overwritten!
  59.  *
  60.  * Examples:
  61.  *    A>SQ GRUMP        makes GRUMP.QQQ on A:
  62.  *    A>SQ D:CRAP.XYZ        makes CRAP.XQZ on A:
  63.  *    A>SQ B: D:CRAP.COM    makes CRAP.CQM on B:
  64.  *    B>SQ X.A C: Y.B        makes X.AQ on B: and Y.BQ on C:
  65.  *
  66.  * The transformations compress strings of identical bytes and
  67.  * then encode each resulting byte value and EOF as bit strings
  68.  * having lengths in inverse proportion to their frequency of
  69.  * occurrence in the intermediate input stream. The latter uses
  70.  * the Huffman algorithm. Decoding information is included in
  71.  * the squeezed file, so squeezing short files or files with
  72.  * uniformly distributed byte values will actually increase size.
  73.  */
  74.  
  75. /* CHANGE HISTORY:
  76.  * 1.3    Close files properly in case of error exit.
  77.  * 1.4    Break up long introductory lines.
  78.  * 1.4    Send introduction only to console.
  79.  * 1.4    Send errors only to console.
  80.  * 1.5  Fix BUG that caused a rare few squeezed files
  81.  *    to be incorrect and fail the USQ crc check.
  82.  *    The problem was that some 17 bit codes were
  83.  *    generated but are not supported by other code.
  84.  *    THIS IS A MAJOR CHANGE affecting TR2.C and SQ.H and
  85.  *    requires recompilation of all files which are part
  86.  *    of SQ. Two basic changes were made: tree depth is now
  87.  *    used as a tie breaker when weights are equal. This
  88.  *    makes the tree shallower. Although that may always be
  89.  *    sufficient, an error trap was added to cause rescaling
  90.  *    of the counts if any code > 16 bits long is generated.
  91.  * 1.5    Add debugging displays option '-'.
  92.  */
  93.  
  94. #define VERSION "1.5   08/29/81"
  95.  
  96. #include <bdscio.h>
  97. #include <dio.h>
  98. #include "sqcom.h"
  99. #include "sq.h"
  100. #define STDOUT 4    /* console only (error) stream */
  101.  
  102. main(argc, argv)
  103. int argc;
  104. char *argv[];
  105. {
  106.     int i,c;
  107.     int getchar();        /* Directed io version */
  108.     char inparg[16];    /* parameter from input */
  109.  
  110.     dioinit(&argc, argv);    /* obey directed to args */
  111.  
  112.     debug = FALSE;
  113.     fprintf(STDOUT,"File squeezer version %s by\n\tRichard Greenlaw\n\t251 Colony Ct.\n\tGahanna, Ohio 43230\n", VERSION);
  114.     fprintf(STDOUT,"Accepts redirection and pipes.\nOmit other parameters for help and prompt\n");
  115.  
  116.     /* Initialize output drive to default drive */
  117.     outdrv[0]  = '\0';
  118.     /* But prepare for a specific drive */
  119.     outdrv[1] = ':';
  120.     outdrv[2] = '\0';    /* string terminator */
  121.  
  122.     /* Process the parameters in order */
  123.     for(i = 1; i < argc; ++i)
  124.         obey(argv[i]);
  125.  
  126.     if(argc < 2) {
  127.         fprintf(STDOUT, "Parameters are from command line or one-at-a-time from standard\ninput and are output drives and input file names. Empty to quit.\n");
  128.         do {
  129.             fprintf(STDOUT, "\n*");
  130.             for(i = 0; i < 16; ++i) {
  131.                 if((c = getchar()) == EOF)
  132.                     c = '\n';    /* fake empty (exit) command */
  133.                 if((inparg[i] = c) == '\n') {
  134.                     inparg[i] = '\0';
  135.                     break;
  136.                 }
  137.             }
  138.             if(inparg[0] != '\0')
  139.                 obey(inparg);
  140.         } while(inparg[0] != '\0');
  141.     }
  142.     dioflush();    /* clean up any directed io */
  143. }
  144.  
  145. obey(p)
  146. char *p;
  147. {
  148.     char *q;
  149.     char outfile[16];    /* output file spec. */
  150.  
  151.     if(*p == '-') {
  152.         /* toggle debug option */
  153.         debug = !debug;
  154.         return;
  155.     }
  156.     if(*(p + 1) == ':') {
  157.         /* Got a drive */
  158.         if(isalpha(*p)) {
  159.             if(*(p+2) == '\0') {
  160.                 /* Change output drive */
  161.                 printf("\nOutput drive =%s",p);
  162.                 outdrv[0] = *p;
  163.                 return;
  164.             }
  165.         } else {
  166.             fprintf(STDOUT, "\nERROR - Ignoring %s", p);
  167.             return;
  168.         }
  169.     }
  170.  
  171.     /* Check for ambiguous (wild-card) name */
  172.     for(q = p; *q != '\0'; ++q)
  173.         if(*q == '*' || *q == '?') {
  174.             fprintf(STDOUT, "\nAmbiguous name %s ignored", p);
  175.             return;
  176.     }
  177.     /* First build output file name */
  178.     outfile[0] = '\0';        /* empty */
  179.     strcat(outfile, outdrv);    /* drive */
  180.     strcat(outfile, (*(p + 1) == ':') ? p + 2 : p);    /* input name */
  181.  
  182.     /* Find and change output file type */
  183.     for(q = outfile; *q != '\0'; ++q)
  184.         if(*q == '.')
  185.             if(*(q + 1) == '\0')
  186.                 *q = '\0';    /* kill trailing dot */
  187.             else
  188.                 switch(*(q+2)) {
  189.                 case 'q':
  190.                 case 'Q':
  191.                     fprintf(STDOUT, "\n%s ignored ( already squeezed?)", p);
  192.                     return;
  193.                 case '\0':
  194.                     *(q+3) = '\0';
  195.                     /* fall thru */
  196.                 default:
  197.                     *(q + 2) = 'Q';
  198.                     goto named;
  199.                 }
  200.     /* No file type */
  201.     strcat(outfile, ".QQQ");
  202. named:
  203.     squeeze(p, outfile);
  204. }
  205.  
  206. squeeze(infile, outfile)
  207. char *infile, *outfile;
  208. {
  209.     int i, c;
  210.     struct _buf inbuff, outbuff;    /* file buffers */
  211.  
  212.     printf("\n%s -> %s: ", infile, outfile);
  213.  
  214.     if(fopen(infile, &inbuff) == ERROR) {
  215.         fprintf(STDOUT, "Can't open %s for input pass 1\n", infile);
  216.         return;
  217.     }
  218.     if(fcreat(outfile, &outbuff) == ERROR) {
  219.         fprintf(STDOUT, "Can't create %s\n", outfile);
  220.         fclose(&inbuff);
  221.         return;
  222.     }
  223.  
  224.     /* First pass - get properties of file */
  225.     crc = 0;    /* initialize checksum */
  226.     printf("analyzing, ");
  227.     init_ncr();
  228.     init_huff(&inbuff);
  229.     fclose(&inbuff);
  230.  
  231.     /* Write output file header with decoding info */
  232.     wrt_head(&outbuff, infile);
  233.  
  234.     /* Second pass - encode the file */
  235.     printf("squeezing, ");
  236.     if(fopen(infile, &inbuff) == ERROR) {
  237.         fprintf(STDOUT, "Can't open %s for input pass 2\n", infile);
  238.         goto closeout;
  239.     }
  240.     init_ncr();    /* For second pass */
  241.  
  242.     /* Translate the input file into the output file */
  243.     while((c = gethuff(&inbuff)) != EOF)
  244.         if(putc(c, &outbuff) == ERROR) {
  245.             fprintf(STDOUT, "ERROR - write failure in %s\n", outfile);
  246.             goto closeall;
  247.         }
  248.     printf(" done.");
  249. closeall:
  250.     fclose(&inbuff);
  251. closeout:
  252.     fflush(&outbuff);
  253.     fclose(&outbuff);
  254. }
  255.